package com.luo.gmall.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.luo.gmall.cart.entity.Cart;
import com.luo.gmall.cart.entity.UserInfo;
import com.luo.gmall.cart.exception.CartException;
import com.luo.gmall.cart.feign.GmallPmsClient;
import com.luo.gmall.cart.feign.GmallSmsClient;
import com.luo.gmall.cart.feign.GmallWmsClient;
import com.luo.gmall.cart.interceptor.LoginInterceptor;
import com.luo.gmall.cart.mapper.CartMapper;
import com.luo.gmall.cart.service.CartAsyncService;
import com.luo.gmall.cart.service.CartService;
import com.luo.gmall.pms.entity.SkuAttrValueEntity;
import com.luo.gmall.pms.entity.SkuEntity;
import com.luo.gmall.sms.vo.ItemSaleVo;
import com.luo.gmall.wms.entity.WareSkuEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author Zoro
 * @date 2021年11月22
 */
@Service
@Slf4j
public class CartServiceImpl implements CartService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Autowired
    private GmallPmsClient gmallPmsClient;

    @Autowired
    public GmallWmsClient gmallWmsClient;

    @Autowired
    public GmallSmsClient gmallSmsClient;


    @Autowired
    private CartAsyncService cartAsyncService;

    /**
     * 用于记录用户的购物信息（hash结构数据）
     */
    private static final String CART_PREFIX = "cart:info:";

    /**
     * 添加购物车将实时的商品价格添加到缓存中（string结构数据）
     */
    private static final String CURRENT_PRICE_PREFIX = "skuId:price:";


    private String getUserId() {
        // 1.获取登录信息
        UserInfo userInfo = loginInterceptor.getUserInfo();
        // 判断是否登录，此时的外层key 有两种情况，
        // ① 如果登录userId存在，以userId为外层key的组成部分
        // ② 未登录，以userKey为外层key的组成部分
        if (userInfo.getUserId() != null) {
            // 已登录
            return userInfo.getUserId().toString();
        } else {
            // 未登录
            return userInfo.getUserKey();
        }
    }

    /**
     * redis hash结构 Map<outerKey, Map<innerKey, value>>
     * 外层 outerKey 用户id
     * 内层 innerKey skuId
     *
     * @param cart 参数
     */
    @Override
    public void addCart(Cart cart) {
        String userId = getUserId();
        String outerKey = CART_PREFIX + userId;
        String innerKey = cart.getSkuId().toString();
        // 2.判断要添加的数据在数据库中是否存在
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(outerKey);
        Boolean exists = hashOps.hasKey(innerKey);
        if (Boolean.TRUE.equals(exists)) {
            // ① 存在，则更新数据库中cart的count数据
            // 获取新增的数量
            BigDecimal count = cart.getCount();
            // 从redis中获取相应的数据
            String cartJson = Objects.requireNonNull(hashOps.get(innerKey)).toString();
            // 将cartJson字符转换成cart对象
            cart = JSON.parseObject(cartJson, Cart.class);
            // 更新数据
            cart.setCount(cart.getCount().add(count));
            // 将更新后的数据保存到mysql和redis中
            // update mysql
            cartAsyncService.updateCart(userId, cart);
//            cartMapper.update(cart, new UpdateWrapper<Cart>().eq("user_id", userId).eq("sku_id", cart.getSkuId()));
        } else {
            // ② 不存在，则添加数据到数据库
            // 获取skuId
            Long skuId = cart.getSkuId();
            // 根据skuId获取skuEntity
            SkuEntity skuEntity = gmallPmsClient.querySkuById(skuId).getData();
            if (skuEntity == null) {
                throw new CartException("商品不存在！");
            } else {
                cart.setDefaultImage(skuEntity.getDefaultImage());
                cart.setPrice(skuEntity.getPrice());
                cart.setTitle(skuEntity.getTitle());
            }
            // 根据skuId获取营销信息
            List<ItemSaleVo> itemSaleVos = gmallSmsClient.queryListSalesBySkuId(skuId).getData();
            cart.setSales(JSON.toJSONString(itemSaleVos));

            // 根据skuId获取库存信息
            List<WareSkuEntity> wareSkuEntities = gmallWmsClient.queryWareSkuBySkuId(skuId).getData();
            if (!CollectionUtils.isEmpty(wareSkuEntities)) {
                boolean isStore = wareSkuEntities.stream().anyMatch(wareSkuEntity -> wareSkuEntity.getStock() - wareSkuEntity.getStockLocked() > 0);
                cart.setStore(isStore);
            }

            // 根据skuId获取销售属性
            List<SkuAttrValueEntity> skuAttrValueEntities = gmallPmsClient.queryCurrentSaleAttrValuesBySkuId(skuId).getData();
            cart.setSaleAttrs(JSON.toJSONString(skuAttrValueEntities));

            cart.setCheck(true);
            cart.setUserId(userId);

            // 加入购物车时，同时加入实时价格缓存
            redisTemplate.opsForValue().set(CURRENT_PRICE_PREFIX + cart.getSkuId(), cart.getPrice().toString());
            // add mysql
            cartAsyncService.insertCart(userId, cart);

        }
        // add redis
        hashOps.put(innerKey, JSON.toJSONString(cart));
    }

    /**
     * 添加购物车回显数据
     *
     * @param cart
     * @return
     */
    @Override
    public Cart queryCart(Cart cart) {
        String userId = getUserId();
        // 回显已添加的购物车信息
        String outerKey = CART_PREFIX + userId;
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(outerKey);
        if (Boolean.TRUE.equals(hashOps.hasKey(cart.getSkuId().toString()))) {
            String cartJson = Objects.requireNonNull(hashOps.get(cart.getSkuId().toString())).toString();
            return JSON.parseObject(cartJson, Cart.class);
        } else {
            throw new CartException("该商品不存在！");
        }
    }


    /**
     * 查询购物车
     *
     * @return
     */
    @Override
    public List<Cart> queryCarts() {
        // 1.获取userKey
        UserInfo userInfo = loginInterceptor.getUserInfo();
        String userKey = userInfo.getUserKey();
        // 2.以userKey为外层key来查询未登录状态的登录购物车的信息
        String unLonginOuterKey = CART_PREFIX + userKey;
        BoundHashOperations<String, Object, Object> unLonginHashOps = redisTemplate.boundHashOps(unLonginOuterKey);
        List<Object> cartJsons = unLonginHashOps.values();
        List<Cart> unLonginCarts = new ArrayList<>();
        if (!CollectionUtils.isEmpty(cartJsons)) {
            unLonginCarts = cartJsons.stream().map(cartJson -> {
                Cart cart = JSON.parseObject(cartJson.toString(), Cart.class);
                // 查询购物车时，同时查询实时价格缓存
                setCurrentPrice(cart);
                return cart;
            }).collect(Collectors.toList());
        }

        // 3.获取UserId，如果为空，则直接返回未登录的购物车
        Long userId = userInfo.getUserId();
        if (userId == null) {
            return unLonginCarts;
        }
        // 4.合并未登录的购物车，到以登陆的购物车中
        // 以userId为外部key
        String loginOuterKey = CART_PREFIX + userId;
        BoundHashOperations<String, Object, Object> loginHashOps = redisTemplate.boundHashOps(loginOuterKey);
        // 判空
        if (!CollectionUtils.isEmpty(unLonginCarts)) {
            // 遍历未登录购物车数据
            unLonginCarts.forEach(unLonginCart -> {
                // 判断该商品是否已经存在购物车中
                if (Boolean.TRUE.equals(loginHashOps.hasKey(unLonginCart.getSkuId().toString()))) {
                    // 获取购物车数据
                    BigDecimal count = unLonginCart.getCount();
                    // 存在，则在存在的商品上累加
                    Cart loginCart = JSON.parseObject(Objects.requireNonNull(loginHashOps.get(unLonginCart.getSkuId().toString())).toString(), Cart.class);
                    loginCart.setCount(loginCart.getCount().add(count));
                    loginCart.setUserId(userId.toString());
                    // 替换到redis中去
                    loginHashOps.put(loginCart.getSkuId().toString(), JSON.toJSONString(loginCart));
                    // 修改数据库
                    cartAsyncService.updateCart(userId.toString(), loginCart);
                } else {
                    // 不存在，则添加到数据库
                    unLonginCart.setUserId(userId.toString());
                    // redis
                    loginHashOps.put(unLonginCart.getSkuId().toString(), JSON.toJSONString(unLonginCart));
                    // mysql
                    cartAsyncService.insertCart(userId.toString(), unLonginCart);
                }
            });
            // 5.删除未登录的购物车
            // redis
            redisTemplate.delete(unLonginOuterKey);
            // mysql
            cartAsyncService.deleteCarts(userKey);
        }
        // 6.查询以登录的购物车
        return Objects.requireNonNull(loginHashOps.values()).stream().map(cartJson -> {
            Cart cart = JSON.parseObject(cartJson.toString(), Cart.class);
            // 查询购物车时，同时查询实时价格缓存
            setCurrentPrice(cart);
            return cart;
        }).collect(Collectors.toList());
    }

    private void setCurrentPrice(Cart cart) {
        String priceStr = Objects.requireNonNull(redisTemplate.opsForValue().get(CURRENT_PRICE_PREFIX + cart.getSkuId()));
        if (!StringUtils.isEmpty(priceStr)) {
            BigDecimal price = new BigDecimal(priceStr);
            cart.setCurrentPrice(price);
        }
    }


    /**
     * 修改购物车选中状态
     *
     * @param cart 前端穿过来的数据 skuId, check
     */
    @Override
    public void updateStatus(Cart cart) {
        // 获取登录状态
        String userId = getUserId();
        // 根据userId从redis中获取要更改的数据
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(CART_PREFIX + userId);
        // 判断更改数据是否存在
        if (Boolean.TRUE.equals(hashOps.hasKey(cart.getSkuId().toString()))) {
            // 获取cartJson数据
            String cartJson = Objects.requireNonNull(hashOps.get(cart.getSkuId().toString())).toString();
            // 解析cartJson数据
            Cart newCart = JSON.parseObject(cartJson, Cart.class);
            // 更改redis中购物车的状态
            newCart.setCheck(cart.getCheck());
            hashOps.put(newCart.getSkuId().toString(), JSON.toJSONString(newCart));
            // 更该mysql中购物车的状态
            cartAsyncService.updateCart(userId, newCart);
        } else {
            throw new CartException("访问异常！");
        }

    }

    /**
     * 修改购物车商品数量
     *
     * @return
     */
    @Override
    public void updateNum(Cart cart) {
        // 获取登录状态
        String userId = getUserId();
        // 根据userId从redis中获取要更改的数据
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(CART_PREFIX + userId);
        // 判断更改数据是否存在
        if (Boolean.TRUE.equals(hashOps.hasKey(cart.getSkuId().toString()))) {
            // 获取cartJson数据
            String cartJson = Objects.requireNonNull(hashOps.get(cart.getSkuId().toString())).toString();
            // 解析cartJson数据
            Cart newCart = JSON.parseObject(cartJson, Cart.class);
            // 更改redis中购物车商品数量
            newCart.setCount(cart.getCount());
            hashOps.put(newCart.getSkuId().toString(), JSON.toJSONString(newCart));
            // 更该mysql中购物车的状态
            cartAsyncService.updateCart(userId, newCart);
        } else {
            throw new CartException("访问异常！");
        }
    }

    /**
     * 删除购物车项
     *
     * @param skuId
     */
    @Override
    public void deleteCart(Long skuId) {
        // 获取登录状态
        String userId = getUserId();
        // 根据userId从redis中获取要更改的数据
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(CART_PREFIX + userId);
        // 删除redis数据
        hashOps.delete(skuId.toString());
        // 删除mysql中购物车的数据
        cartAsyncService.deleteCart(userId, skuId);
    }


    @Override
    public List<Cart> queryCartsByUserId(Long userId) {
        BoundHashOperations<String, Object, Object> boundHashOps = redisTemplate.boundHashOps(CART_PREFIX + userId);
        List<Object> cartJsons = boundHashOps.values();
        if (!CollectionUtils.isEmpty(cartJsons)) {
            return cartJsons.stream().map(cartJson -> JSON.parseObject(cartJson.toString(), Cart.class)).filter(Cart::getCheck).collect(Collectors.toList());

        }
        return null;
    }


    @Override
    @Async
    public void executor1() {

        try {
            log.info("③ executor1开始执行。。。");
            TimeUnit.SECONDS.sleep(4);
            int i = 1 / 0;
            log.info("④ executor1执行结束。。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    @Async
    public void executor2() {

        try {
            log.info("③ executor2开始执行。。。");
            TimeUnit.SECONDS.sleep(5);
            log.info("⑤ executor2执行结束。。");
        } catch (InterruptedException e) {
            e.printStackTrace();

        }

    }

}
